﻿/*------------------------------------------------------------------
Copyright (c) 2008 visualSingularity.net. All rights reserved.

This file is part of Hyperbolic control.

Hyperbolic control is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

Hyperbolic control is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Windows.Media;
using Hyperbolic.Vectors;

namespace Hyperbolic.Model
{
    /// <summary> The BaseNodeModel class implements encapsulation of a <see cref="ITemplatedNode"/> for the model. 
    /// It keeps the original euclidian coordinates of the node. 
    /// It implements the Composite design pattern.
    /// </summary>
    public class NodeModel : INotifyPropertyChanged
    {

        #region fields
        private IBindingNode _node;        
        private ITreeModel _model;
        private CompositeNodeModel _parent;
        private NodeModel _sibling;
        private EuclidianVector _oldZe = new EuclidianVector();
        private EuclidianVector _ze = new EuclidianVector();
        private double _weight = 1.0;  // part of space taken by this node
        private bool _isExpanded;        
        #endregion

        #region properties

        public bool IsExpanded
        {
            get { return _isExpanded; }
            set
            {
                _isExpanded = value;
                OnPropertyChanged("IsExpanded");
            }
        }

        /// <summary> Gets the encapsulated node.
        /// </summary>
        public IBindingNode Node
        {
            get
            {
                return _node;
            }
        }

        /// <summary> Gets the euclidian coordinates.
        /// </summary>
        public EuclidianVector EuclidianCoordinates
        {
            get { return _ze; }
            set
            {
                _ze = value;
                this.OnPropertyChanged("EuclidianCoordinates");
            }
        }

        public ScreenVector ScreenCoordinates
        {
            get
            {
                ScreenVector __zs = new ScreenVector();
                __zs.ProjectionEtoS(_ze, _model.ContainerOrigin, _model.ContainerBounds);
                return __zs;
            }
        }

        /// <summary> Gets the encapsulated tree model.
        /// </summary>
        public ITreeModel Model
        {
            get { return _model; }
            set { _model = value; }
        }

        /// <summary> Gets or sets the weight of this node.
        /// </summary>
        public double Weight
        {
            get
            {
                return _weight;
            }
            set
            {
                _weight = value;
            }
        }

        /// <summary> Gets the parent of this node.
        /// </summary>
        public CompositeNodeModel Parent
        {
            get
            {
                return _parent;
            }
        }

        public NodeModel Sibling
        {
            get { return _sibling; }
            set { _sibling = value; }
        }

        /// <summary> Returns <code>true</code> if this node is not an instance of CompositeNodeModel, <code>false</code> otherwise.
        /// </summary>
        public virtual bool IsLeaf
        {
            get { return true; }
        }

        /// <summary> Gets the old coordinates of this node.
        /// </summary>
        public EuclidianVector OldEuclidianCoordinates
        {
            get
            {
                return _oldZe;
            }
        }

        #endregion

        #region ctor


        /// <summary> Constructor for root node.
        /// </summary>
        /// <param name="node">The encapsulated <see cref="INode"/>.</param>
        /// <param name="model">The tree model using this <see cref="NodeModel"/>.</param>
        public NodeModel(IBindingNode node, ITreeModel model)
            : this(node, null, model)
        {
        }

        /// <summary> Constructor used by nodes that are not the root node
        /// </summary>
        /// <param name="node">The encapsulated <see cref="INodeView"/>.</param>
        /// <param name="parent">The parent node.</param>
        /// <param name="model">The tree model using this <see cref="NodeModel"/>.</param>
        public NodeModel(IBindingNode node, CompositeNodeModel parent, ITreeModel model)
        {
            _ze = new EuclidianVector();
            _node = node;
            _parent = parent;
            _model = model;
        }

        #endregion

        #region methods

        /// <summary> Layouts the nodes in the hyperbolic space.
        /// </summary>
        public void LayoutHyperbolicTree()
        {
            this.Layout(0.0, Math.PI, _model.Length);
        }

        /// <summary> Layout this node in the hyperbolic space.
        /// First set the point at the right distance, then translate by father's coordinates.
        /// Then, compute the right angle and the right width.
        /// </summary>
        /// <param name="angle">The angle from the x axis</param>
        /// <param name="width">The angular width to divide, / 2</param>
        /// <param name="length">The parent-child length.</param>
        public virtual void Layout(double angle, double width, double length)
        {
            // Nothing to do for the root node
            if (_parent == null)
            {
                return;
            }

            EuclidianVector __zp = _parent.EuclidianCoordinates;

            // We first start as if the parent was the origin.
            // We still are in the hyperbolic space.
            _ze.X = length * Math.Cos(angle);
            _ze.Y = length * Math.Sin(angle);

            // Then translate by parent's coordinates
            _ze.Translate(__zp);
            _oldZe.Copy(_ze);
        }

        public virtual void DrawBranches(DrawingContext dc) {}

        /// <summary> Transform this node by the given transformation.
        /// </summary>
        /// <param name="t">The transformation.</param>
        public virtual void Transform(HyperbolicTransformation t)
        {
            _ze.Copy(_oldZe);
            _ze.Transform(t);
            this.OnPropertyChanged("EuclidianCoordinates");
            this.OnPropertyChanged("ScreenCoordinates");
        }

        /// <summary> Ends the translation.
        /// </summary>
        public virtual void EndTranslation()
        {
            _oldZe.Copy(_ze);
        }

        #endregion

        #region  tostring

        public override string ToString()
        {
            string __result = _node.Name +
                            ":" + _ze +
                            ";Weight = " + _weight;
            return __result;
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        #endregion
    }
}
